/**
 * @description: proxy仍然未实现深度监听，需要通过递归代理的方式监听。
 * @params {target} 拦截的目标对象
 * @params {handler} 定制拦截行为
 * @return {*}
 */
const target = {}
const proxy = new Proxy(target, {
	// handler 方法 ..
})

// 方法
Proxy.revocable() // 返回可取消的Proxy实例(返回{ proxy, revoke }，通过revoke()取消代理)

// handler拦截方式
get() //拦截对象属性读取
set() //拦截对象属性设置，返回布尔
has() //拦截对象属性检查k in obj，返回布尔
deleteProperty() //拦截对象属性删除delete obj[k]，返回布尔
defineProperty() //拦截对象属性定义Object.defineProperty()、Object.defineProperties()，返回布尔
ownKeys() //拦截对象属性遍历for -in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()，返回数组
getOwnPropertyDescriptor() //拦截对象属性描述读取Object.getOwnPropertyDescriptor()，返回对象
getPrototypeOf() //拦截对象原型读取instanceof、Object.getPrototypeOf()、Object.prototype.__proto__、Object.prototype.isPrototypeOf()、Reflect.getPrototypeOf()，返回对象
setPrototypeOf() //拦截对象原型设置Object.setPrototypeOf()，返回布尔
isExtensible() //拦截对象是否可扩展读取Object.isExtensible()，返回布尔
preventExtensions() //拦截对象不可扩展设置Object.preventExtensions()，返回布尔
apply() //拦截Proxy实例作为函数调用proxy()、proxy.apply()、proxy.call()
construct() //拦截Proxy实例作为构造函数调用new proxy()

// 应用场景
Proxy.revocable() //不允许直接访问对象，必须通过代理访问，一旦访问结束就收回代理权不允许再次访问
get() //读取未知属性报错、读取数组负数索引的值、封装链式操作、生成DOM嵌套节点
set() //数据绑定(Vue数据绑定实现原理)、确保属性值设置符合要求、防止内部属性被外部读写
has() //隐藏内部属性不被发现、排除不符合属性条件的对象
deleteProperty() //保护内部属性不被删除
defineProperty() //阻止属性被外部定义
ownKeys() //保护内部属性不被遍历

// 重点难点
// 要使Proxy起作用，必须针对实例进行操作，而不是针对目标对象进行操作
// 没有设置任何拦截时，等同于直接通向原对象
// 属性被定义为不可读写 / 扩展 / 配置 / 枚举时，使用拦截方法会报错
// 代理下的目标对象，内部this指向Proxy代理

// TODO Proxy数据劫持(简易版)
const obj = {
	name: '刘逍',
	age: 20,
	like: ['eating', 'playing'],
}

function reactive(obj) {
	if (typeof obj !== 'object' && obj != null) {
		return obj
	}
	return new Proxy(obj, {
		// 读取属性的时候会调用getter
		get(target, propName) {
			//第一个参数为代理的源对象,等同于上面的Obj参数。第二个参数为读取的那个属性值
			const res = Reflect.get(target, propName)
			console.log(`有人读取p对象里的${propName}属性`)
			return typeof res === 'object' ? reactive(res) : res // 如果深层次数据变化需要再次监听才能监听到vue3中
		},
		// 添加和修改属性的时候会调用setter
		set(target, propName, value) {
			//参数等同于get，第三个参数为修改后的属性值
			console.log(`有人修改了p对象里的${propName}属性,值为${value},需要去修改视图`)
			Reflect.set(target, propName, value)
		},
		// 删除属性时，调用deleteProperty
		deleteProperty(target, propName) {
			// 参数等同于get
			console.log(`有人删除了p对象里的${propName}属性，需要去修改视图`)
			return Reflect.deleteProperty(target, propName)
		},
	})
}
const p = reactive(obj)
p.name = '张三'
p.age = '18'
p.like[0] = 'running'
